﻿Public Class KeyboardHook
#Region "Declaraciones"
    'Constantes
    Private Const HC_ACTION As Integer = 0
    Private Const WH_KEYBOARD_LL As Integer = 13
    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Private Const WM_SYSKEYDOWN = &H104
    Private Const WM_SYSKEYUP = &H105

    'Estructuras Keypress
    Private Structure KBDLLHOOKSTRUCT
        Public vkCode As Integer
        Public scancode As Integer
        Public flags As Integer
        Public time As Integer
        Public dwExtraInfo As Integer
    End Structure
    'Funciones API
    Private Declare Function SetWindowsHookEx Lib "user32" _
    Alias "SetWindowsHookExA" _
    (ByVal idHook As Integer, _
    ByVal lpfn As KeyboardProcDelegate, _
    ByVal hmod As Integer, _
    ByVal dwThreadId As Integer) As Integer

    Private Declare Function CallNextHookEx Lib "user32" _
    (ByVal hHook As Integer, _
    ByVal nCode As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As KBDLLHOOKSTRUCT) As Integer

    Private Declare Function UnhookWindowsHookEx Lib "user32" _
    (ByVal hHook As Integer) As Integer

    'Delegaciones del teclado
    Private Delegate Function KeyboardProcDelegate _
    (ByVal nCode As Integer, _
    ByVal wParam As Integer, _
    ByRef lParam As KBDLLHOOKSTRUCT) As Integer

    'Eventos KeyPress down & up 
    Public Shared Event KeyDown(ByVal Key As Keys)
    Public Shared Event KeyUp(ByVal Key As Keys)

    Private Shared KeyHook As Integer
    ''KeyHookDelegate
    Private Shared KeyHookDelegate As KeyboardProcDelegate
#End Region

    Public Sub New()
        ''Hookeo de bajo nivel al teclado
        KeyHookDelegate = New KeyboardProcDelegate(AddressOf KeyboardProc)
        KeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
    End Sub

    Private Shared Function KeyboardProc(ByVal nCode As Integer, ByVal wParam As Integer, ByRef lParam As KBDLLHOOKSTRUCT) As Integer
        ''tecla oprimida
        If (nCode = HC_ACTION) Then
            Select Case wParam
                Case WM_KEYDOWN, WM_SYSKEYDOWN ''Evento Keydown
                    ''Le mandamos el Evento Al formulario
                    RaiseEvent KeyDown(CType(lParam.vkCode, Keys))
                Case WM_KEYUP, WM_SYSKEYUP ''EventoKeyUp
                    ''Le mandamos el Evento Al formulario
                    RaiseEvent KeyUp(CType(lParam.vkCode, Keys))
            End Select
        End If
        Return CallNextHookEx(KeyHook, nCode, wParam, lParam)
    End Function

    Protected Overrides Sub Finalize()
        ''Al finalizar quitamos el hook
        UnhookWindowsHookEx(KeyHook)
        MyBase.Finalize()
    End Sub
End Class